﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Web.Mvc;
using System.Text;
using System.ComponentModel.DataAnnotations;

namespace Xoohoo.ModelValidation.Attributes
{
    /// <summary>
    /// 验证字符串的字节长度是否在某个范围
    /// </summary>
    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
    public class ByteLengthAttribute : ValidationAttribute, IClientValidatable
    {
        #region Properties

        public int MaximumLength { get; set; }

        public int MinimumLength { get; set; }

        #endregion

        public ByteLengthAttribute(int maximumLength)
            : base(() => "The field {0} must be a string with a maximum length of {1}.")
        {
            MaximumLength = maximumLength;
        }

        private void EnsureLegalLengths()
        {
            if (MaximumLength < 0)
            {
                throw new InvalidOperationException("The maximum length must be a nonnegative integer.");
            }
            if (MaximumLength < MinimumLength)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "The maximum value '{0}' must be greater than or equal to the minimum value '{1}'.", new object[] { this.MaximumLength, this.MinimumLength }));
            }
        }

        public override string FormatErrorMessage(string name)
        {
            EnsureLegalLengths();
            string format = "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.";
            return string.Format(CultureInfo.CurrentCulture, format, new object[] { name, this.MaximumLength, this.MinimumLength });
        }

        public override bool IsValid(object value)
        {
            EnsureLegalLengths();
            if (value == null) return true;

            int length = GetByteLength((string)value);

            return length >= MinimumLength && length <= MaximumLength;
        }

        private int GetByteLength(string source)
        {
            byte[] bytes = Encoding.Unicode.GetBytes(source);

            int result = 0;
            for (int i = 0; i < bytes.Length; i++)
            {
                //  偶数位置，如0、2、4等，为UCS2编码中两个字节的第一个字节
                if (i % 2 == 0)
                {
                    //  在UCS2第一个字节时n加1
                    result++;
                }
                else if (bytes[i] > 0)
                {
                    // 当UCS2编码的第二个字节大于0时，该UCS2字符为汉字，一个汉字算两个字节
                    result++;
                }
            }
            return result;
        }

        #region IClientValidatable 成员

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            return new ModelClientValidationRule[]{      
                new ModelClientValidationByteLengthRule(FormatErrorMessage(metadata.GetDisplayName())
                    , MinimumLength
                    , MaximumLength)
            };
        }

        #endregion
    }

}
